package com.ikingtech.platform.service.system.dict.controller;

import com.ikingtech.framework.sdk.context.event.TenantDeleteEvent;
import com.ikingtech.framework.sdk.context.event.TenantInitEvent;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.dict.api.DictApi;
import com.ikingtech.framework.sdk.dict.model.DictDTO;
import com.ikingtech.framework.sdk.dict.model.DictQueryParamDTO;
import com.ikingtech.framework.sdk.enums.system.dictionary.DictEnum;
import com.ikingtech.framework.sdk.enums.system.dictionary.DictItemEnum;
import com.ikingtech.framework.sdk.enums.system.dictionary.DictTypeEnum;
import com.ikingtech.framework.sdk.log.embedded.annotation.OperationLog;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.web.annotation.ApiController;
import com.ikingtech.platform.service.system.dict.entity.DictDO;
import com.ikingtech.platform.service.system.dict.entity.DictItemDO;
import com.ikingtech.platform.service.system.dict.exception.DictExceptionInfo;
import com.ikingtech.platform.service.system.dict.service.DictItemService;
import com.ikingtech.platform.service.system.dict.service.DictService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;

/**
 * 系统管理-字典管理
 * <p>1. 字典数据需按租户隔离
 *
 * @author tie yan
 */
@Slf4j
@RequiredArgsConstructor
@ApiController(value = "/system/dict", name = "系统管理-字典管理", description = "系统管理-字典管理")
public class DictController implements DictApi {

    private final DictService service;

    private final DictItemService itemService;

    @Override
    @OperationLog(value = "新增字典", dataId = "#_res.getData()")
    @Transactional(rollbackFor = Exception.class)
    public R<String> add(DictDTO dict) {
        String tenantCode = Me.tenantCode();
        if (Boolean.TRUE.equals(this.service.nameExist(dict.getName(), tenantCode))) {
            throw new FrameworkException(DictExceptionInfo.DUPLICATE_DICT_NAME);
        }

        DictDO entity = Tools.Bean.copy(dict, DictDO.class);
        entity.setId(Tools.Id.uuid());
        entity.setTenantCode(tenantCode);
        entity.setCode(Tools.Str.isBlank(dict.getCode()) ? entity.getId() : dict.getCode());
        entity.setType(DictTypeEnum.BUSINESS.name());
        this.service.save(entity);

        return R.ok(entity.getId());
    }

    @Override
    @OperationLog(value = "删除字典")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> delete(String id) {
        DictDO entity = this.service.getById(id);
        if (null == entity) {
            return R.ok();
        }
        if (DictTypeEnum.SYSTEM.name().equals(entity.getType())) {
            throw new FrameworkException(DictExceptionInfo.DELETE_SYSTEM_DICT_IS_NOT_ALLOWED);
        }
        this.service.removeById(id);
        this.itemService.removeByDictId(id);
        return R.ok();
    }

    @Override
    @OperationLog(value = "更新字典")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> update(DictDTO dict) {
        if (!this.service.exist(dict.getId())) {
            throw new FrameworkException(DictExceptionInfo.DICT_NOT_FOUND);
        }
        if (Boolean.TRUE.equals(this.service.nameExist(dict.getId(), dict.getName(), Me.tenantCode()))) {
            throw new FrameworkException(DictExceptionInfo.DUPLICATE_DICT_NAME);
        }
        this.service.updateById(Tools.Bean.copy(dict, DictDO.class));
        return R.ok();
    }

    @Override
    public R<List<DictDTO>> page(DictQueryParamDTO queryParam) {
        return R.ok(this.service.listPage(queryParam, Me.tenantCode()).convert(this::modelConvert));
    }

    @Override
    public R<List<DictDTO>> all() {
        // 根据当前用户的租户标识查询
        return R.ok(this.modelConvert(this.service.listByTenantCode(Me.tenantCode())));
    }

    @Override
    public R<DictDTO> detail(String id) {
        return R.ok(this.modelConvert(this.service.getById(id)));
    }

    private List<DictDTO> modelConvert(List<DictDO> entities) {
        return Tools.Coll.convertList(entities, this::modelConvert);
    }

    private DictDTO modelConvert(DictDO entity) {
        DictDTO dict = Tools.Bean.copy(entity, DictDTO.class);
        if (null != dict.getType()) {
            dict.setTypeName(DictTypeEnum.valueOf(entity.getType()).description);
        }
        return dict;
    }

    @EventListener
    public void tenantInitEventListener(TenantInitEvent event) {
        List<DictDO> entities = Tools.Array.convertList(DictEnum.values(), value -> {
            DictDO entity = new DictDO();
            entity.setId(Tools.Id.uuid());
            entity.setName(value.description);
            entity.setRemark(value.description);
            entity.setCode(value.name());
            entity.setType(DictTypeEnum.SYSTEM.name());
            entity.setTenantCode(event.getCode());
            return entity;
        });
        Map<String, String> dictMap = Tools.Coll.convertMap(entities, DictDO::getCode, DictDO::getId);
        List<DictItemDO> itemEntities = Tools.Array.convertList(DictItemEnum.values(), value -> {
            DictItemDO entity = new DictItemDO();
            entity.setId(Tools.Id.uuid());
            entity.setLabel(value.description);
            entity.setValue(value.name());
            entity.setRemark(value.description);
            entity.setDictCode(value.dict.name());
            entity.setDictId(dictMap.get(value.dict.name()));
            entity.setFullPath(value.name());
            entity.setTenantCode(event.getCode());
            return entity;
        });
        if (Tools.Coll.isNotBlank(entities)) {
            this.service.saveBatch(entities);
            if (Tools.Coll.isNotBlank(itemEntities)) {
                this.itemService.saveBatch(itemEntities);
            }
        }
    }

    @EventListener
    public void tenantDeleteEventListener(TenantDeleteEvent event) {
        this.service.removeByTenantCode(event.getCode());
        this.itemService.removeByTenantCode(event.getCode());
    }
}
